package sketch;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import sketch.util.Checker;
import sketch.util.Randomness;

/**
 * SMethod is SketchMethod
 * */
public final class SMethod {
	
	//public final Class<?> clazz;
	
	public final SClass sclazz;
	
	//public final List<Method> methods = new ArrayList<Method>();
	
	public final List<String> methodNames;
	
	public final List<Method> methods;
	
	public String randomMethod() {
		Checker.checkTrue(!methodNames.isEmpty(), "There is no methods existed");
		return Randomness.randomSetMember(methodNames);
	}
	
	public boolean isConstructor() {
		boolean isConstructor = (methodNames.size() == 1 && methodNames.get(0).equals("<init>"));
		return isConstructor;
	}
	
	public static SMethod constructor(Class<?> clazz) {
		Checker.checkNull(clazz, "The class name could not be null!");
		return new SMethod(clazz, "<init>");
	}
	
	public static SMethod instance(Object receiver, String methodName) {
		Checker.checkNull(receiver, "The receiver object could not be null!");
		return new SMethod(receiver.getClass(), methodName);
	}
	
	public static SMethod instance(Object receiver, String... methodNames) {
		Checker.checkNull(receiver, "The receiver object could not be null!");
		return new SMethod(receiver.getClass(), methodNames);
	}

	public SMethod(Class<?> clazz, String methodName) {
		Checker.checkNull(clazz, "The class name could not be null!");
		//this.clazz = clazz;
		this.sclazz = new SClass(clazz);
		this.methodNames = new ArrayList<String>();
		this.methods = new ArrayList<Method>();
		Method m = this.sclazz.findMethod(methodName);
		Checker.checkNull(m, "There is no method: " + methodName + " in class: " + clazz);
		//add method
		this.methodNames.add(methodName);
		this.methods.add(m);
		Checker.checkTrue(this.methodNames.size() == this.methods.size(),
				"The size of method name list: " + this.methodNames.size() + " does not equal to " +
						" the size of method list: " + this.methods.size());
	}
	
	public SMethod(Class<?> clazz, String[] methodNames) {
		Checker.checkNull(clazz, "The class name could not be null!");
		Checker.checkNull(methodNames, "The method names could not be null!");
		this.sclazz = new SClass(clazz);
		this.methodNames = new ArrayList<String>();
		this.methods = new ArrayList<Method>();
		for(String methodName : methodNames) {
			Checker.checkNull(methodName, "The method name could not be null!");
			this.methodNames.add(methodName);
			//find method
			Method m = this.sclazz.findMethod(methodName);
			Checker.checkNull(m, "There is no method: " + methodName + " in class: " + clazz);
			this.methods.add(m);
		}
		Checker.checkTrue(this.methodNames.size() == this.methods.size(),
				"The size of method name list: " + this.methodNames.size() + " does not equal to " +
						" the size of method list: " + this.methods.size());
	}
	
	public String[] getMethodNames() {
		return methodNames.toArray(new String[0]);
	}
	
	public int getMethodNum() {
		return methodNames.size();
	}
	
	public Method findMethod(String method) {
		int index = this.methodNames.indexOf(method);
		if(index == -1) {
			return null;
		}
		return this.methods.get(index);
	}
	
	
	
	public String methodsToString() {
		StringBuilder sb = new StringBuilder();
		
		sb.append("[");
		for(int i = 0; i < this.methodNames.size(); i++) {
			if(i != 0) {
				sb.append(",");
			}
			sb.append(this.methodNames.get(i));
		}
		sb.append("]");
		
		return sb.toString();
	}
	
}